package gl.java.umsp.room;

import gl.java.network.transport.kcp.umsp.UmspKcpServer;
import gl.java.umsp.*;
import gl.java.umsp.bean.Room;
import gl.java.umsp.bean.User;
import gl.java.umsp.event.Event;
import gl.java.umsp.event.EventPublisher;
import gl.java.umsp.event.EventServerConfig;
import gl.java.umsp.room.framework.RoomServerFramework;
import gl.java.umsp.room.websocket.RoomWebSocketFrameHandler;
import gl.java.umsp.websocket.UmspWebSocketService;
import gl.java.util.TextUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import lombok.extern.slf4j.Slf4j;

import javax.script.ScriptEngine;
import java.util.Date;

/**
 * 设计文档见RoomService.md
 */
@Slf4j
public class RoomServiceBase implements IRoomService {
    RoomServiceBase() {

    }

    protected RoomServerFramework framework;

    protected void listenWebSocketPort(final RoomServiceConfig config) {
        new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(100);
                    new UmspWebSocketService(config.portWebSocket, "/ws", new Class[]{RoomWebSocketFrameHandler.class, RoomConnectionHandler.class}).run();
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("stop ,room websocket service exception:  " + e);
                }

            }
        }.start();
    }
    protected void listenUdpPort(final RoomServiceConfig config) {
        new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(100);
                    new UmspKcpServer().main(new String[]{String.valueOf(config.portUdp)});
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("stop ,room websocket service exception:  " + e);
                }

            }
        }.start();
    }

    protected void listenBinaryServerPort(RoomServiceConfig config) {

        // 启动网络消息监听服务
        GenericFutureListener<Future<? super Void>> afterConnect = new GenericFutureListener<Future<? super Void>>() {
            @Override
            public void operationComplete(Future<? super Void> future) throws Exception {
                log.info("=========  =======================  ==========");
                log.info("=========  RoomService has started  ==========");
                log.info("=========  =======================  ==========");
                EventPublisher.pub(Event.ROOM_SERVICE_ON_LINE, RoomServiceConfig.getDefaultConfig().toString());
                framework.getImp().start(RoomServiceConfig.getDefaultConfig());
            }
        };
        GenericFutureListener<Future<? super Void>> afterClose = new GenericFutureListener<Future<? super Void>>() {
            @Override
            public void operationComplete(Future<? super Void> future) throws Exception {
                log.info(" start ");
                EventPublisher.pub(Event.ROOM_SERVICE_OFF_LINE, RoomServiceConfig.getDefaultConfig().toString());
                framework.getImp().stop();
            }
        };
        listenNetWork(UmspConfig.DEFAULT_ROOM_PORT, afterConnect, afterClose);
    }

    public void listenNetWork(final int port, GenericFutureListener<? extends Future<? super Void>> afterConnect, GenericFutureListener<? extends Future<? super Void>> afterClose) {
        EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap(); // (2)
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class) // (3)
                    .childHandler(new ChannelInitializer<SocketChannel>() { // (4)
                        @Override
                        public void initChannel(SocketChannel ch)
                                throws Exception {
                            ch.pipeline().addLast(
                                    new UmspConnectionHandler(),
                                    new UmspMessageUnpacker(),
                                    new UmspMessagePacker(),
                                    new RoomConnectionHandler(),
                                    new RoomMessageDispatcher()
                            );
                        }
                    }).option(ChannelOption.SO_BACKLOG, 128)//The maximum queue length for incoming connection indications (a request to connect) is set to the backlog parameter. If a connection indication arrives when the queue is full, the connection is refused.
                    .childOption(ChannelOption.TCP_NODELAY, true) // (5)
                    .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)
            ChannelFuture f = b.bind(port).sync(); // (7)
            f.addListener(afterConnect);
            f.channel().closeFuture().addListener(afterClose).sync();
        } catch (Exception e) {
            log.warn("stop,listening network  exception:" + e);
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
            log.info("Room MessageQueueService  Stop listen the network  at :" + new Date());
        }
    }

    @Override
    public void start(Config config) {

        RoomServiceConfig roomServiceConfig = null;
        if (config != null && config instanceof RoomServiceConfig) {
            roomServiceConfig = (RoomServiceConfig) config;
        }
        //开启线程启动WebSocket服务
        listenWebSocketPort(roomServiceConfig);
        listenUdpPort(roomServiceConfig);
        //启动 Umsp 二进制协议服务
        listenBinaryServerPort(roomServiceConfig);

    }

    public void regRoomServer(RoomServiceConfig roomServiceConfig) {
        log.info("[RegRoomServer]: " + roomServiceConfig.toString());
        EventPublisher.pub(Event.ROOM_SERVICE_ON_LINE, roomServiceConfig.toString());
    }


    @Override
    public void stop() {

    }


    @Override
    public boolean onUserEnter(Room room, User user) {
        if (TextUtil.isNotEmpty(room.roomID)) {
            RoomUserChangeState roomUserChange = new RoomUserChangeState(user.userID, room.roomID, "enter", room.getRoomUserList());
            String message = roomUserChange.toString();
            EventPublisher.pub(Event.USER_ENTER_ROOM, message);
            RoomService.INSTANCE.broadcast(new UmspHeader(Umsp.CMD_ROOM_USER_CHANGED, message), room);
//            ScriptEngine.invokeFunction("onUserEnter", user, room);
            return framework.getImp().onUserEnter(room, user);
        }
        return false;

    }

    @Override
    public boolean onUserExit(Room oldRoom, User user) {
        log.info("user Exit" + user);

        Room room = RoomPool.getInstance().findRoomByUserID(user.userID);
        if (room != null) {
//        RoomPool.getInstance().removeUser(user.userID);
//        Map<Integer, User> roomUserList = room.getRoomUserList();
            room.removeUserFromCurrentUserList(user.userID);
            RoomUserChangeState roomUserChange = new RoomUserChangeState(user.userID, room.roomID, "exit", room.getRoomUserList());
            String message = roomUserChange.toString();
            EventPublisher.pub(Event.USER_EXIT_ROOM, message);
            RoomService.INSTANCE.broadcast(new UmspHeader(Umsp.CMD_ROOM_USER_CHANGED, message), room);
//            ScriptEngine.invokeFunction("onUserExit", user, room);
            return framework.getImp().onUserExit(room, user);
        } else {
            log.warn("not exist user:" + user.userID);
        }
        return false;
    }


    @Override
    public boolean onUserSendMsg(UmspHeader msg, Room room) {
        msg.cmd = Umsp.CMD_MSG_RSP;
//        log.info("onUserSendMsg:" + new String(msg.payload));
//        if (ScriptManger.invokeFunction("onUserSendMsg", msg, room) != ScriptManger.NULL)
//            return true;

        if (framework.getImp().onUserSendMsg(msg, room))
            return true;

        RoomService.INSTANCE.broadcast(msg, room);
        return false;
    }
}
